	TITLE	System-dependent installation overlay for ZMP
	SUBTTL	Author: Ron Murray

; System-dependent installation overlay for ZMP
; Author: Ron Murray

;	Name	ZMPOVH89.Z80

;	Dated Nov 11, 1988

;	Written by - Ron Murray, c/o Z-Node 62, 061-9-450-0200
;		     Perth, Western Australia.

;	Modified by - Ted May,  116B Laval St., Vanier, Ont., Canada
;				(613)741-0862
;				for the H89-H19 terminal
;				with a Hays compatable 2400 baud modem
;				(US Robotics Courier 2400)

; 89/04/12 - Modified to ZMP v1.5		- George Conover
;	Modified to ZMP v1.4 standard Ron Murray Nov 11, 88
;	Modified to ZMP v1.3 standard Ron Murray Oct 11, 88
;	Modified to ZMP v1.2 standard Ron Murray Sept 15, 88
;
;-----------------------------------------------------------------------
;
;	System-dependent code overlay for ZMODEM
;
;   Insert your own code as  necessary	in  this  file.  Code  contained
; herein  has been written in Z80 code for use with M80 or SLR programs.
; Assemble as follows:

;	SLR ZMO-MH01/h
;	MLOAD ZMP.COM=ZMODEM.COM,ZMO-MH01.HEX
;  or
;	M80 =ZMO-MH01.Z80
;	RELHEX ZMO-MH01
;	MLOAD ZMP.COM=ZMODEM.COM,ZMO-MH01.HEX


; (Don't use L80 without changing the source  for  assembly  as  a  CSEG
; file.)

;-----------------------------------------------------------------------

;    Code contained herein has been written in Z80 code for use with M80.
; Once assembled, convert to hex with RELHEX and use MLOAD to overlay it
; over the main ZMPX.COM file to produce your very own ZMP.COM.

;-----------------------------------------------------------------------

; Notes on modifying this file:

;    Hi-Tech C requires that functions do not change either index register
; (IX or IY). If your overlay requires either of these to be changed, ensure
; they are restored to their original values on return.
;    Since collecting parameters from C functions can be tricky, only change
; the parts marked 'Insert your own code here'.  Do NOT modify the jump
; table at the start.  Do NOT modify the entry/exit sections of each
; function.  Do NOT pass 'GO'.  Do NOT collect $200.
;    Apart from defining modem functions, this file also defines terminal
; characteristics.  Most have been set up for ADM-3A (with a few of my own
; additions).  Modify to suit your own terminal.  An inline print routine
; is provided for printing strings in the usual way: usage is

;	CALL	PRINT
;	DEFB	'required string',0

;    Don't forget to set your clock speed at the clkspd variable.

;    If you find your overlay exceeds the maximum size (currently 0400h),
; you will have to re-compile the whole thing.  Good luck.  You might try
; informing us if you need to do this: if too many people need to do it, we
; haven't allowed enough room.

; Ron Murray Aug 15, 88

;-----------------------------------------------------------------------

NO	EQU	0
YES	EQU	NOT NO


CLKSPD	EQU	2		; Processor clock speed in MHz
DEBUG	EQU	NO		; To allow debugging of overlay with
				; Z8E etc.

; Set the following two equates to the drive and user area which will
; contain ZMP's .OVR files, .CFG file, .FON file and .HLP file.  Set
; both to zero (null) to locate them on the drive from which ZMP was
; invoked.

OVERDRIVE EQU	0		; Drive to find overlay files on ('A'-'P')
OVERUSER EQU	0		; User area to find files

;-----------------------------------------------------------------------

; NOT user-set variables


USERDEF	EQU	0145H		; Origin of this overlay.  This address
				; should not change with subsequent
				; revisions.
MSPEED	EQU	003CH		; Location of current baud rate.
OVSIZE	EQU	0400H		; Maximum size of this overlay

	.Z80			; use z80 code

	ASEG			; absolute

	 IF	DEBUG
	ORG	100H		; So you can debug it with CEBUG, ZSID, etc.
	 ELSE
	ORG	USERDEF
	 ENDIF

ESC	EQU	1BH
CTRLQ	EQU	11H
CR	EQU	0DH
LF	EQU	0AH
BDOS	EQU	5

; The following define the H8/89 SIO Modem Port addresses...

DPORT	EQU	0D8H		; Data port (base port)
LSPDIV	EQU	DPORT		; LSP baud rate divisor if DLAB set
IENREG	EQU	DPORT+1		; Interupt Enable register
MSPDIV	EQU	DPORT+1		; MSP baud rate divisor if DLAB set
IIDREG	EQU	DPORT+2		; Interrupt identification register (r/o)
LCPORT	EQU	DPORT+3		; Line Control register
MCPORT	EQU	DPORT+4		; Modem Control register
LSPORT	EQU	DPORT+5		; Line Status register
MSPORT	EQU	DPORT+6		; Modem Status register

; Line Control (LCPORT) bits - - - - - - - - - - - - - - - - -

WLS0	EQU	00000001B	; Word length select 0
WLS1	EQU	00000010B	; Word length select 1
STB	EQU	00000100B	; Stop bit select
PEN	EQU	00001000B	; Parity Enable (0 for disabled)
PES	EQU	00010000B	; Even parity select (0 for odd)
SPE	EQU	00100000B	; Stick parity
BRKS	EQU	01000000B	; Break set (set break)
DLAB	EQU	10000000B	; Divisor Latch Access Bit

; Modem Control (MCPORT) bits - - - - - - - - - - - - - - - - -

MCBASE	EQU	00000000B	; Basic setup: no DTR or RTS
DTR	EQU	00000001B	; Data Terminal Ready
RTS	EQU	00000010B	; Request To Sent
OUT1 	EQU	00000100B	; Aux Output #1
OUT2 	EQU	00001000B	; Aux Output #2
TSTLP	EQU	00010000B	; Sets 8250 Test Loop condition

; Line Status (LSPORT) bits - - - - - - - - - - - - - - - - - -

DAV	EQU	00000001B	; Data Available (and bit to test)
ORUN	EQU	00000010B	; Overrun Error (rec'd was char overwritten
RPERR	EQU	00000100B	; Parity Error
FERR	EQU	00001000B	; Framing Error (checks for valid stop bit)
BRKD	EQU	00010000B	; Break Detect
TBMT	EQU	00100000B	; Transmit Buffer Empty (and bit to test)
TSRE	EQU	01000000B	; Transmit Shift Register Empty (r/o)

; Modem Status (MSPORT) bits - - - - - - - - - - - - - - - - - -

DCTS	EQU	00000001B	; Delta Clear To Send
DDSR	EQU	00000010B	; Delta Data Set Ready
TERI	EQU	00000100B	; Trailing Edge Ring Indicator
DRLSD	EQU	00001000B	; Delta Receive Line Signal Detect
CTS	EQU	00010000B	; Clear To Send
DSR	EQU	00100000B	; Data Set Ready
RDET	EQU	01000000B	; Ring Detect
RLSD	EQU	10000000B	; Carrier Detect
RESET	EQU	00000001B	; Output Reset Code

; Baud rate divisor values - - - - - - - - - - - - - - - - - - -

BD110	EQU	1047		; 110    bps
BD300	EQU	384		; 300    bps
BD450	EQU	256		; 450    bps
BD600	EQU	192		; 600    bps
BD710	EQU	162		; 710    bps
BD1200	EQU	96		; 1200   bps
BD2400	EQU	48		; 2400   bps
BD4800	EQU	24		; 4800   bps
BD9600	EQU	12		; 9600   bps
BD19200	EQU	6		; 19,200 bps
BD38400 EQU	3		; 38,400 bps
BD57600 EQU	2		; 57,600 bps

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

;Jump table for the overlay: do NOT change this
JUMPTAB:
	JP	SCRNPR		; screen print
	JP	MRD		; modem read with timeout
	JP	MCHIN		; get a character from modem
	JP	MCHOUT		; send a character to the modem
	JP	MORDY		; test for tx buffer empty
	JP	MIRDY		; test for character received
	JP	SNDBRK		; send break
	JP	CURSADD		; cursor addressing
	JP	CLS		; clear screen
	JP	INVON		; inverse video on
	JP	INVOFF		; inverse video off
	JP	HIDE		; hide cursor
	JP	SHOW		; show cursor
	JP	SAVECU		; save cursor position
	JP	RESCU		; restore cursor position
	JP	MINT		; service modem interrupt
	JP	INVEC		; initialise interrupt vectors
	JP	DINVEC		; de-initialise interrupt vectors
	JP	MDMERR		; test uart flags for error
	JP	DTRON		; turn DTR on
	JP	DTROFF		; turn DTR OFF
	JP	INIT		; initialise uart
	JP	WAIT		; wait seconds
	JP	MSWAIT		; wait milliseconds
	JP	USERIN		; User-defined entry routine
	JP	USEROUT		; User-defined exit routine
	JP	GETVARS		; Get system variables
  	JP	SETPORT		; Set port (0 or 1)
;
; Spare jumps for compatibility with future versions
;
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
	JP	SPARE		; Spares for later use
	JP	SPARE		; spares for later use
	JP	SPARE		; spares for later use
	JP	SPARE		; spares for later use


; Main code starts here


VERSN:	DEFB	'===> Ver. 1.5.1520.24.11.88.ZMPOVL <==='

; Screen print function

SCRNPR:
				; <== Insert your own code here
	CALL	PRINT
	DB	'This function not supported.',CR,LF,0
				; <== End of your own code
SPARE:	RET

; User-defined entry routine: leave empty if not needed

USERIN:	RET

; User-defined exit routine: leave empty if not needed

USEROUT:RET


; Get a character from the modem: return in HL
; (It is not necessary to test for status)

MCHIN:
				; <== Insert your own code here
	IN	A,(DPORT)	; to get the character in A
				; <== End of your own code
	LD	L,A		; put in HL
	LD	H,0
	OR	A		; set/clear Z
	RET

;Send a character to the modem

MCHOUT:
	LD	HL,2		; get the character
	ADD	HL,SP
	LD	A,(HL)		; in A
				; <== Insert your own code here
	OUT	(DPORT),A
				; <== End of your own code
	RET			; done

;Test for output ready: return TRUE (1) in HL if ok

MORDY:
				; <== Insert your own code here
	LD	HL,0		; Assume that the Transmit Buffer is full
	IN	A,(LSPORT)	; Get Line Status
	AND	TBMT		; Is the Transmit Buffer Empty?
	JR	Z,TB.BUSY	; No, skip
	LD	L,1		; Yes, HL = 1 (TRUE)
TB.BUSY:			; <== End of your own code
	LD	A,L		; Set/clear Z
	OR	A
	RET

;Test for character at modem: return TRUE (1) in HL if so

MIRDY:
				; <== Insert your own code here
	LD	HL,0		; Assume that no data is available
	IN	A,(LSPORT)	; Get Line Status
	AND	DAV		; Is Data Available?
	JR	Z,NO.DAV	; No, skip
	LD	L,1		; Yes, HL = 1 (TRUE)
NO.DAV:				; <== End of your own code
	LD	A,L		; set/clear Z
	OR	A
	RET

;Send a break to the modem: leave empty if your system can't do it

SNDBRK:
				; <== Insert your own code here

; This routine sends a 300 ms Break Tone and sets DTR low for the 
; same length of time to disconnect the modem at both ends

	XOR	A		; Clear reg A to
	OUT	(MCPORT),A	; Set DTR and RTS low
	LD	A,BRKS		; Set Break Tone
	OUT	(LCPORT),A	; to go to 'break' level
		

	LD	HL,300		; wait 300 ms
	CALL	WAITHLMS

DSP	EQU	$+1		; Inline code modification (data, stop, parity)
	LD	A,WLS0+WLS1	; Set 8 data bits, 1 stop bit, no parity
	OUT	(LCPORT),A
	LD	A,DTR		; Set DTR for proper Modem output level
	OUT	(MCPORT),A
				; to restore
				; <== End of your own code
	RET

;Test UART flags for error: return TRUE (1) in HL if error

MDMERR:
				; <== Insert your own code here
	LD	HL,0		; Set HL = FALSE (0)
				; <== End of your own code
	LD	A,L		; set/clear Z
	OR	A
	RET

;Turn DTR (and optionally RTS) ON.

DTRON:
				; <== Insert your own code here
	LD	A,DTR		; Set DTR
	OUT	(MCPORT),A
				; <== End of your own code
	RET

;Turn DTR ( and RTS?) OFF

DTROFF:
				; <== Insert your own code here
	XOR	A		; Clear reg A to
	OUT	(MCPORT),A	; Set DTR and RTS low
				; <== End of your own code
	RET

;Initialise the UART

INIT:
	LD	HL,2		; get parameters
	ADD	HL,SP
	EX	DE,HL
	CALL	GETPARM		; in HL
	LD	(BRATE),HL	; baud rate
	CALL	GETPARM
	LD	(PARITY),HL	; parity
	CALL	GETPARM
	LD	(DATA),HL	; data bits
	CALL	GETPARM
	LD	(STOP),HL	; stop bits
				; <== Insert your own code here
	DI			; Disable CPU Interrupts
	LD	A,(BRATE)	; A = Baud rate number (see table below)
	CP	11+1		; Allow speeds to 57,600 bps
	JR	NC,BADRATE	; Skip if invalid baud rate
	LD	(MSPEED),A	;  else save for current BRATE
	LD	E,A
	LD	D,0		; DE = Baud rate number
	LD	HL,BAUD.RATES	; HL -> Start of baudrate divisor table	
	ADD	HL,DE		; Add it twice, each
	ADD	HL,DE		;  Divisor value occupies two bytes
	LD	A,(HL)		; HL -> Desired divisor value, load HL
	INC	HL		;	 indirect through HL [(HLIHL)]
	LD	H,(HL)
	LD	L,A		; HL = Divisor value
	LD	A,D		; A = 0
	OUT	(IENREG),A	; Disable 8250 interrupts
	LD	A,DLAB		; Set DLAB so we can use the baud rates
	OUT	(LCPORT),A	;  divisors
	LD	A,L		; A = LSP of divisor
	OUT	(LSPDIV),A	; Write divisor's "LSP"
	LD	A,H		; A = MSP of divisor
	OUT	(MSPDIV),A	; Write divisor's "MSP"
	LD	A,(DATA)	; Get data bits count (7 or 8)
	CP	7		; Is it 7 data bits?
	LD	A,10B		; Assume it is
	JR	Z,IS7BITS	; Yes, skip
	LD	A,11B		; No, is 8 data bits, A = 11B
IS7BITS:
	LD	D,A		; Save data bits
	LD	A,(STOP)	; Get number of desired stop bits (1 or 2)
	CP	1		; Want one stop bit?
	LD	A,0		; Assume so
	JR	Z,ONE.STOP	; Yes, skip
	LD	A,100B		; No, set bit 2 - 2 stop bits wanted
ONE.STOP:
	OR	D		; OR in data bits info
	LD	D,A		;  and save data bits and stop bit info
	LD	A,(PARITY)	; Get desired parity indicator (N, E or O)
	LD	E,A		;  and save it for possible later use
	CP	'N'		; Want no parity?
	LD	A,D		; Assume so, A = data, stop, and parity (D,S,P)
	JR	Z,HAVE.BITS	; Yes, skip
	LD	A,E		; Get back desired parity indicator
	CP	'E'		; Want Even parity?
	LD	A,PEN+PES	; Assume so, set even parity
	OR	D		;  and OR in data bits and stop bit info
	JR	Z,HAVE.BITS	; Yes, skip
	LD	A,PEN		; No, has to be Odd parity, set it
	OR	D		;  and OR in data bits and stop bit info
HAVE.BITS:
	LD	(DSP),A		; Set it for resetting after disconnect
	OUT	(LCPORT),A	; Set desired word, stop and parity
	LD	A,DTR		; Set DTR for proper MODEM operation
	OUT	(MCPORT),A
BADRATE:			; Skips above to here if baud rate bad
	EI			; Restore CPU interrupts
				; using values below
				; <== End of your own code
	RET

STOP:	DEFW	1		; Stop bits (will be 1 or 2)
PARITY:	DEFW	'N'		; Parity (will be 'N', 'E' or 'O')
DATA:	DEFW	8		; Data bits (will be 7 or 8)
BRATE:	DEFW	6		; Baud rate:

;-----------------------------------------------------------------------

; Values of BRATE for each baud rate

; baud rate    BRATE

;   110 	 0
;   300 	 1
;   450 	 2
;   600 	 3
;   710 	 4
;  1200 	 5
;  2400 	 6
;  4800 	 7
;  9600 	 8
; 19200 	 9
; 38400 	10
; 57600 	11

;-----------------------------------------------------------------------

; Baud rate divisor table

BAUD.RATES:
	DEFW	BD110		;  0 -    110 baud
	DEFW	BD300		;  1 -    300
	DEFW	BD450		;  2 -    450
	DEFW	BD600		;  3 -    600
	DEFW	BD710		;  4 -    710
	DEFW	BD1200		;  5 -  1,200
	DEFW	BD2400		;  6 -  2,400
	DEFW	BD4800		;  7 -  4,800
	DEFW	BD9600		;  8 -  9,600
	DEFW	BD19200		;  9 - 19,200
	DEFW	BD38400		; 10 - 38,400
	DEFW	BD57600		; 11 - 57,600

;
; Set the port. ZMP supplies either 0 or 1 as a parameter.
;
setport:
	ld	hl,2		; get port number
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL (values are 0 and 1)

				; <== Insert your own code here

				; <== End of your own code
	ret
;****************************************************************************
;Video terminal sequences: these are for ADM-3A: Modify as you wish
;Cursor addressing: 	(modified for H19/89)

CURSADD:
	LD	HL,2		; get parameters
	ADD	HL,SP
	EX	DE,HL
	CALL	GETPARM		; in HL
	LD	(ROW),HL	; row
	CALL	GETPARM
	LD	(COL),HL	; column
				; <== Insert your own code here
				; using values in row and col
	CALL	PRINT
	DEFB	ESC,'Y',0	; H19/89 leadin
	LD	A,(ROW)		; row first
	ADD	A,' '		; add offset
	CALL	COUT
	LD	A,(COL)		; sane for column
	ADD	A,' '
	CALL	COUT
				; <== end of your own code
	RET

ROW:	DEFS	2		; row
COL:	DEFS	2		; column


;Clear screen:

CLS:
	CALL	PRINT
	DEFB	ESC,'E',0
	RET

;Inverse video on:

INVON:
	CALL	PRINT
	DEFB	ESC,'p',0
	RET

;Inverse video off:

INVOFF:
	CALL	PRINT
	DEFB	ESC,'q',0
	RET

;Turn off cursor:

HIDE:
	CALL	PRINT
	DEFB	ESC,'x5',0
	RET

;Turn on cursor:

SHOW:
	CALL	PRINT
	DEFB	ESC,'y5',0
	RET

;Save cursor position:

SAVECU:
	CALL	PRINT
	DEFB	ESC,'j',0
	RET

;Restore cursor position:

RESCU:
	CALL	PRINT
	DEFB	ESC,'k',0
	RET

;****************************************************************************

;Service modem interrupt:

MINT:
	RET			; my system doesn't need this

;Initialise interrupt vectors:

INVEC:
	RET			; ditto

;De-initialise interrupt vectors:

DINVEC:
	RET			; ditto

;****************** End of user-defined code ********************************
; Don't change anything below this point. We needed some assembly language
; stuff for speed, and this seemed like a good place to put it.

;Modem character test for 100 ms

MRD:
	PUSH	BC		; save bc
	LD	BC,100		; set limit
MRD1:
	CALL	MIRDY		; char at modem?
	JR	NZ,MRD2		; yes, exit
	LD	HL,1		; else wait 1ms
	CALL	WAITHLMS
	DEC	BC		; loop till done
	LD	A,B
	OR	C
	JR	NZ,MRD1
	LD	HL,0		; none there, result=0
	XOR	A
MRD2:
	POP	BC
	RET

; Inline print routine: destroys A

PRINT:
	EX	(SP),HL		; get address of string
PLOOP:
	LD	A,(HL)		; get next
	INC	HL		; bump pointer
	OR	A		; done if zero
	JR	Z,PDONE
	CALL	COUT		; else print
	JR	PLOOP		; and loop
PDONE:
	EX	(SP),HL		; restore return address
	RET			; and quit

;
;Output a character in A to the console
;
COUT:
	PUSH	BC		; save regs
	PUSH	DE
	PUSH	HL
	LD	E,A		; character to E
	LD	C,2
	CALL	BDOS		; print it
	POP	HL
	POP	DE
	POP	BC
	RET

;Wait(seconds)

WAIT:
	LD	HL,2
	ADD	HL,SP
	EX	DE,HL		; get delay size
	CALL	GETPARM
				; fall thru to..
;Wait seconds in HL

WAITHLS:
	PUSH	BC		; Save BC
	PUSH	DE		; DE
	PUSH	IX		; And IX
	LD	IX,0		; Then point IX to 0
				; So we don't upset memory-mapped I/O
;
; Calculate values for loop constants. Need to have two loops to avoid
; 16-bit overflow with clock speeds above 9 MHz.
;
OUTERVAL EQU	(CLKSPD/10)+1
INNERVAL EQU	(6667/OUTERVAL)*CLKSPD
;
WAIT10:	LD	B,OUTERVAL
;
WAIT11:	LD	DE,INNERVAL
;
WAIT12:	BIT	0,(IX)		; Time-wasters
	BIT	0,(IX)
	BIT	0,(IX)		; 20 T-states each
	BIT	0,(IX)
	BIT	0,(IX)
	BIT	0,(IX)
	DEC	DE
	LD	A,E
	LD	A,D
	OR	E
	JR	NZ,WAIT12	; 150 T-states per inner loop
	DJNZ	WAIT11		; Decrement outer loop
	DEC	HL		; Ok, decrement count in hl
	LD	A,H
	OR	L
	JR	NZ,WAIT10
	POP	IX		; Done -- restore ix
	POP	DE		; De
	POP	BC		; And bc
	RET

;Wait milliseconds

MSWAIT:
	LD	HL,2
	ADD	HL,SP
	EX	DE,HL		; get delay size
	CALL	GETPARM
				; fall thru to..
;Wait milliseconds in HL

WAITHLMS:
	PUSH	DE
W1MS0:
	LD	DE,39 * CLKSPD
W1MS1:
	DEC	DE
	LD	A,D
	OR	E
	JR	NZ,W1MS1
	DEC	HL
	LD	A,H
	OR	L
	JR	NZ,W1MS0
	POP	DE
	RET

;Get next parameter from (de) into hl

GETPARM:
	EX	DE,HL		; get address into hl
	LD	E,(HL)		; get lo
	INC	HL
	LD	D,(HL)		; then hi
	INC	HL		; bump for next
	EX	DE,HL		; result in hl, address still in de
	RET


; Get address of user-defined variables

GETVARS:
	LD	HL,USERVARS
	RET

USERVARS:
	DEFW	OVERDRIVE	; OVR etc. drive/user
	DEFW	OVERUSER

	 IF	($ - JUMPTAB) GT OVSIZE
TOOBIG:	JP	ERRVAL		; Overlay too large!
	 ENDIF

	END
